package com.gitee.fsmxm.thinking.in.concurrent.basic;

import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 异步转同步
 */
public class _05_ReentrantLock_AsynToSyn {

    public static class Client {
        private DefaultFuture request(String param, int timeout) {
            DefaultFuture defaultFuture = new DefaultFuture(timeout);
            doRequest(param, defaultFuture);
            return defaultFuture;
        }
        // 异步请求
        private void doRequest(String param, DefaultFuture defaultFuture) {
            new Thread(() -> {
                try {
                    int millis = ThreadLocalRandom.current().nextInt(100, 2000);
                    System.out.println("request time: " + millis);
                    Thread.sleep(millis); // 模拟请求耗时
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                defaultFuture.doReceive("Ok.");
            }).start();
        }

        public String invoke(String param, int timeout) {
            return request(param, timeout).get();
        }
    }

    public static class DefaultFuture {
        private final int timeout; // 超时返回时间
        private final ReentrantLock lock;
        private final Condition done;
        public DefaultFuture(int timeout) {
            this.timeout = timeout;
            lock = new ReentrantLock();
            done = lock.newCondition();
        }

        Object response;

        /**
         * 请求结果
         * @return "Ok." 成功，或者 "timeout.." 超时，或者 null 无结果
         */
        public String get() {
            lock.lock();
            try {
                while (!isDone()) {
                    if (!done.await(timeout, TimeUnit.MILLISECONDS)) {
                        return "timeout..";
                    }
                }
                return response.toString();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
            return null;
        }

        private boolean isDone() {
            return response != null;
        }

        public void doReceive(Object response) {
            lock.lock();
            try {
                this.response = response;
                done.signal();
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        Client client = new Client();
        String res = client.invoke("param", 1500);
        System.out.println("return: " + res);
    }

}
